package com.vae.virtualanimationexperiment.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vae.virtualanimationexperiment.constants.SysConstants;
import com.vae.virtualanimationexperiment.domain.ResponseResult;
import com.vae.virtualanimationexperiment.domain.entity.Info;
import com.vae.virtualanimationexperiment.domain.entity.LoginUser;
import com.vae.virtualanimationexperiment.domain.entity.User;
import com.vae.virtualanimationexperiment.domain.form.LoginForm;
import com.vae.virtualanimationexperiment.domain.form.RegisterForm;
import com.vae.virtualanimationexperiment.domain.form.RetrievePwdForm;
import com.vae.virtualanimationexperiment.enums.AppHttpCodeEnum;
import com.vae.virtualanimationexperiment.exception.SystemException;
import com.vae.virtualanimationexperiment.mapper.InfoMapper;
import com.vae.virtualanimationexperiment.mapper.UserMapper;
import com.vae.virtualanimationexperiment.service.UserService;
import com.vae.virtualanimationexperiment.utils.BeanCopyUtils;
import com.vae.virtualanimationexperiment.utils.JwtUtil;
import com.vae.virtualanimationexperiment.utils.RedisCache;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 用户表(User)表服务实现类
 *
 * @author makejava
 * @since 2023-03-05 20:39:25
 */
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Value("${spring.mail.username}")
    private String fromEmail;

    @Resource
    private JavaMailSender javaMailSender;

    @Resource
    private RedisCache redisCache;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private InfoMapper infoMapper;

    @Override
    public ResponseResult login(LoginForm loginForm) {
        Authentication authentication =
                new UsernamePasswordAuthenticationToken(loginForm.getUserName(), loginForm.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authentication);
        if (Objects.isNull(authenticate)) {
            throw new SystemException(AppHttpCodeEnum.LOGIN_ERROR);
        }
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();

        User user = loginUser.getUser();
        if (Objects.isNull(user)) {
            throw new SystemException(AppHttpCodeEnum.LOGIN_ERROR);
        }
        Long userId = user.getId();

        redisCache.setCacheObject(SysConstants.PRE_LOGIN_USER_REDIS + userId, loginUser);

        String jwt = JwtUtil.createJWT(userId.toString());


        user.setPassword("");
        Map<String, Object> map = new HashMap<>();
        map.put("token", jwt);
        map.put("userInfo", user);

        return ResponseResult.okResult(map);
    }


    @Override
    public ResponseResult logout() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long userId = loginUser.getUser().getId();

        redisCache.deleteObject(SysConstants.PRE_LOGIN_USER_REDIS + userId);
        SecurityContextHolder.getContext().setAuthentication(null);

        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult getVerificationCode(String email) {
        if (!email.matches("[\\w\\.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+")) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_FORMAT_ERROR);
        }
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName, email);
        User user = getOne(queryWrapper);
        if (Objects.nonNull(user)) {
            throw new SystemException(AppHttpCodeEnum.USER_EXIT);
        }

        //验证码是否已经发送
        String emailKey = SysConstants.EMAIL_CODE + email;
        String emailCode = redisCache.getCacheObject(emailKey);
        if (StringUtils.hasText(emailCode)) {
            return ResponseResult.errorResult(309, "验证码已发送");
        }

        //发送验证码
        String sendEmailCode = null;
        try {
            sendEmailCode = sendCode(email, "注册验证码为: ", "虚拟仿真技术在动画镜头语言中的应用");
        } catch (Exception e) {
            return ResponseResult.errorResult(500, "邮箱不存在");
        }

        //将验证码存入redis
        redisCache.setCacheObject(emailKey, sendEmailCode, 1, TimeUnit.MINUTES);

        return ResponseResult.okResult();
    }

    @Transactional
    @Override
    public ResponseResult regist(RegisterForm registerForm) {

        if (!StringUtils.hasText(registerForm.getPassword())) {
            return ResponseResult.errorResult(305, "密码不能为空");
        }
        if (!StringUtils.hasText(registerForm.getUserName())) {
            return ResponseResult.errorResult(306, "用户名不能为空");
        }

        boolean isRegisted = judgeRegisted(registerForm.getUserName());
        if (isRegisted) {
            return ResponseResult.errorResult(308, "该邮箱已经注册");
        }

        //验证码校验
        String redisEmailCode = redisCache.getCacheObject(SysConstants.EMAIL_CODE + registerForm.getUserName());
        if (!StringUtils.hasText(redisEmailCode)) {
            return ResponseResult.errorResult(309, "验证码失效");
        }
        if (!redisEmailCode.equals(registerForm.getCode())) {
            return ResponseResult.errorResult(310, "验证码错误");
        }

        //保存用户信息
        User user = BeanCopyUtils.copyBean(registerForm, User.class);
        user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
        user.setNickName("user_" + UUID.randomUUID().toString().substring(0, 6));
        user.setAvatar("http://118.89.125.143:9000/permanent-bucket/zhihu.jpg");
        save(user);

        //实验人数+1
        Info info = infoMapper.selectById(1);
        info.setExperimentNum(info.getExperimentNum() + 1);
        infoMapper.updateById(info);

        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult getForgetCode(String email) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName, email);
        User one = getOne(queryWrapper);
        if (Objects.isNull(one)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.USER_NOT_EXIT);
        }

        //发送验证码
        String sendEmailCode = sendCode(email, "验证码为: ", "虚拟动画仿真实验——找回密码");

        //将验证码存入redis
        String emailKey = SysConstants.FORGET_CODE + email;
        redisCache.setCacheObject(emailKey, sendEmailCode, 1, TimeUnit.MINUTES);

        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult retrievePassword(RetrievePwdForm form) {
        if (!form.getPassword().equals(form.getConfirm())) {
            throw new SystemException(AppHttpCodeEnum.NEW_CON_PASSWORD_NOT_MATCH);
        }

        String code = redisCache.getCacheObject(SysConstants.FORGET_CODE + form.getUserName());
        if (!code.equals(form.getCode())) {
            throw new SystemException(AppHttpCodeEnum.CODE_ERROR);
        }

        String encode = new BCryptPasswordEncoder().encode(form.getPassword());
        User user = User.builder()
                .password(encode)
                .build();
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName, form.getUserName());
        update(user, queryWrapper);

        return ResponseResult.okResult();
    }

    @Override
    public Map<String, String> getNickNameAndAvatarByUserId(Long userId) {
        Map<String, Map<String, String>> map = baseMapper.getNickNameAndAvatarByUserId(userId);
        Set<String> keySet = map.keySet();
        if (Objects.isNull(keySet)) {
            return new HashMap<>();
        }
        Iterator<String> iterator = keySet.iterator();
        if (!iterator.hasNext()) {
            return new HashMap<>();
        }
        String next = iterator.next();
        return StringUtils.hasText(next) ? map.get(next) : new HashMap<>();
    }

    private boolean judgeRegisted(String userName) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUserName, userName);
        User user = getOne(queryWrapper);
        if (Objects.isNull(user)) {
            return false;
        }
        return true;
    }

    private String sendCode(String email, String preInfo, String subject) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom(fromEmail);
        mailMessage.setTo(email);
        mailMessage.setSubject(subject);
        int sendEmailCode = (int) (Math.random() * ((999999 - 100000 + 1) + 100000));
        String context = preInfo + sendEmailCode + ",一分钟内有效，请妥善保管!";
        mailMessage.setText(context);
        javaMailSender.send(mailMessage);

        return sendEmailCode + "";
    }
}

